#include <reactor.h>

#ifdef _WIN32

static struct fd_set fdset_read;
static struct fd_set fdset_write;
static struct event *event_table[65536];
static int called = 0;

static int win32_select_init()
{
    memset(&fdset_read, 0, sizeof(struct fd_set));
    memset(&fdset_write, 0, sizeof(struct fd_set));
    return 0;
}

static 
int win32_select_add(struct event *ev)
{
    if(ev->watch & EV_READ)
        FD_SET(ev->sockfd, &fdset_read);
    if(ev->watch & EV_WRITE)
        FD_SET(ev->sockfd, &fdset_write);
    if(event_table[ev->sockfd] != 0) {
        return -1;
    }
    event_table[ev->sockfd] = ev;
    return 0;
}

static int win32_select_mod(struct event *ev)
{
    if(!ev->sockfd) {
        return -1;
    }
    if(ev->watch & EV_READ)
        FD_SET(ev->sockfd, &fdset_read);
    if(ev->watch & EV_WRITE)
        FD_SET(ev->sockfd, &fdset_write);
    if(!(ev->watch & EV_READ))
        FD_CLR(ev->sockfd, &fdset_read);
    if(!(ev->watch & EV_WRITE))
        FD_CLR(ev->sockfd, &fdset_write);
    return 0;
}

static int win32_select_del(struct event *ev)
{
    if(event_table[ev->sockfd] != 0) {
        event_table[ev->sockfd] = 0;
        printf("Delete socket:%p\n", ev);
        if(ev->watch & EV_READ)
            FD_CLR(ev->sockfd, &fdset_read);
        if(ev->watch & EV_WRITE)
            FD_CLR(ev->sockfd, &fdset_write);
    }
    return 0;
}

static int win32_select_process(struct event_base *base)
{
    int i = 0;
    int err = 0;
    struct event *ev;
    fd_set fdread;
    fd_set fdwrite;

    called++;

    if(!(called % 100000)) {
        __("Select: called %d times\n", called);
    }
    
retry:
    fdread = fdset_read;
    fdwrite = fdset_write;
    err = select(0, &fdread, &fdwrite, 0, 0);
    if(err == -1) {
        int n;
        int s = sizeof(int);
        
        if(!(fdset_read.fd_count) && !(fdset_write.fd_count)) {
            goto out;
        }
        
        for(i = 0;i < fdset_read.fd_count;i++) {
            err = getsockopt(fdset_read.fd_array[i], SOL_SOCKET, SO_TYPE, (char *)&n, &s);
            if(err == -1) {
                win32_select_del(event_table[fdset_read.fd_array[i]]);
            }
        }
        printf("Select: Error!\n");
        goto retry;
    }
    if(err == 0) {
        printf("Select: timeout.\n");
        goto out;
    }
    
    __("Select: FD count = %d\n", fdread.fd_count);
    for(i = 0;i < fdread.fd_count;i++) {
        int signal = EV_READ;
        ev = event_table[fdread.fd_array[i]];
        ev->signal = signal;
        queue_add_tail(&ev->signal_node, &base->signal_queue);
    }
    for(i = 0;i < fdwrite.fd_count;i++) {
        int signal = EV_WRITE;
        ev = event_table[fdwrite.fd_array[i]];
        if(ev->signal) {
            ev->signal |= signal;
        }else {
            ev->signal = signal;
            queue_add_tail(&ev->signal_node, &base->signal_queue);
        }
    }
out:
    return err;
}

struct event_syscall win32_select = {
    "WIN32_Select", 
    &win32_select_init, 
    &win32_select_add, 
    &win32_select_mod, 
    &win32_select_del, 
    &win32_select_process
};

#else

struct event_syscall win32_select = {
    "WIN32_Select", 
    0, 
    0, 
    0, 
    0, 
    0
};

#endif